#include "comm.h"

#include <arpa/inet.h>
#include <errno.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>


typedef struct client {
    struct client * prev;
    struct client * next;
    int fd;
    char ip[100];
    unsigned short port;
} client;

client * client_list = 0;

static void broadcast(char * msg, int len)
{
    if (msg[len-1] != '\n' ) {
        msg[len] = '\n';
        len += 1;
    }

    printf("%s", msg);
    client * x = client_list;
    client_list = 0;
    while (x) {
        client * c = x;
        dlist_remove(x, c);
        dlist_insert_tail(client_list, c);
        send(c->fd, msg, len, 0);
    }
}

void on_recv(network * net, int fd, void * ctx)
{
    char buff[1024] = {0}; 
    client * c = (client*)ctx;
    int len = 0;
    int addr_len = sprintf(buff, "[%s:%u] : ", c->ip, c->port);
retry:
    switch (len = recv(fd, buff + addr_len, sizeof(buff) - addr_len -1, 0)) {
        case -1:
            if (errno == EAGAIN || errno == EWOULDBLOCK ) {
                return;
            }
            if (errno == EINTR ) {
                goto retry;
            }
        case 0:
            len = sprintf(buff + addr_len, "socket %d closed: %s\n",
                    fd, (len==0)?"closed by peer":strerror(errno));
            network_del(net, fd);
            dlist_remove(client_list, c);
            release_mem(c, sizeof(client_list));
            close(fd);

            len += addr_len;
            broadcast(buff, len);
            return;
        default:
            break;
    }

    len += addr_len;
    broadcast(buff, len);
    goto retry;
}

void on_accept(network * net, int listen_fd, int new_fd,
        const char * ip, unsigned short port, void * ctx)
{
    char buff[1024] = {0};
    int len = sprintf(buff, "[%s:%u] : socket %d connected\n", ip, port, new_fd);
    client * c = (client*)get_mem(sizeof(client));
    if (c == 0) {
        printf("get_mem failed: %s\n", strerror(errno));
        close(new_fd);
        return;
    }

    c->fd = new_fd;
    strcpy(c->ip, ip);
    c->port = port;

    if (network_set(net, new_fd, on_recv, 0, c) != 0) {
        printf("network_set failed");
        release_mem(c, sizeof(client));
        close(new_fd);
        return;
    }

    broadcast(buff, len);
    dlist_insert_tail(client_list, c);
}

void on_timer(network * net, time_t t, void * ctx)
{
//    printf("on_timer\n");
}

int main(int argc, char * argv[])
{
    const char * ip = "127.0.0.1";
    int port = 10000;
    if (argc == 2) {
        port = atoi(argv[1]);
    } else if (argc == 3) {
        ip = argv[1];
        port = atoi(argv[2]);
    }

    network * net = network_create(NETWORK_LT);
    if (net == 0) {
        perror("network_create failed");
        return 1;
    }

    network_timer(net, on_timer, 0);

    int fd = network_listen(net, ip, port, on_accept, 0);
    if (fd < 0) {
        fprintf(stderr, "network_listen %s:%d failed: %s",
                ip, port, strerror(errno));
        return -1;
    }
    printf("listen on %s:%d success\n", ip, port);

    network_loop(net);

    network_destory(net);

    return 0;
}
